package cooperated

import (
	"bytes"
	"fmt"
	"math/rand"
	"runtime/debug"

	"gddgame.cc/galaxy/common/namespaces/cooperate"
	"gddgame.cc/galaxy/common/namespaces/cooperate/structure"
	"gddgame.cc/galaxy/common/service/pathfinding"
	"gddgame.cc/galaxy/project/breeder/breeder/model"
	"gddgame.cc/galaxy/utils"
)

func (logic *TypeMap) needPathOrBlock(info *structure.ScheduleInfo, content *cooperate.MoveScheduleContent, selfIdentity int, current []byte, position []byte, visible TargetVisible, nextArrive bool, preSeat bool, gapTime int) *pathfinding.StrategyBlockResponse {
	// 判断绕路还是
	//blockTime := BlockTime         // + utils.ToString(visible)
	//blockIdentity := BlockIdentity // + utils.ToString(visible)
	//stopStatus := StopStatus       // utils.ToString(visible)

	// 隐身：部队进入不可见状态，任何移动以外的行为都会解除隐身状态，隐身状态不会自动攻击
	// 隐身部队靠近敌对部队1格内会被发现：findTarget里面判断攻击距离至少1，所以会有，进行属性修改
	// 隐身部队和同盟部队可以正常通过，但是不能在同一格：不能在同一格
	// 隐身部队和隐身部队会相互发现，但是不显示，只是提示有危险，然后自动寻路绕路

	request := &pathfinding.StrategyBlockRequest{
		Goal:     content.Plan[len(content.Plan)-1],
		Visible:  utils.ToInt(visible),
		Arrive:   nextArrive,
		Item:     info.Target.GetItem(),
		PreSeat:  preSeat,
		GapTime:  gapTime,
		From:     current,
		Position: position,
		Mask: &pathfinding.StrategyMask{
			Self:     content.SelfMask,
			Other:    content.OtherMask,
			Identity: selfIdentity,
			Filter:   nil,
			Pass:     nil,
			//TargetPositions: content.TargetPositions,
		},
	}
	resB := logic.CanAndPreMove(current, position, info.Time, request)
	if resB == nil {
		return nil
	}
	response := &pathfinding.StrategyBlockResponse{}
	if _, err := response.UnmarshalMsg(resB); err != nil {
		fmt.Println("need path response", err)
	}
	//fmt.Println("response", response)
	return response
}

func (logic *TypeMap) continuePath(current []byte, next []byte, position []byte, filter [][]byte, pass [][]byte, info *structure.ScheduleInfo, content *cooperate.MoveScheduleContent, selfIdentity int, visible TargetVisible) bool {
	var positions [][]byte
	isStart := false
	// 如果next为空，则已current为起点
	if next == nil || len(next) == 0 {
		next = current
		isStart = true
	}
	positions = logic.OnPathFinding(next, position, &pathfinding.StrategyMask{
		Self:             content.SelfMask,
		Other:            content.OtherMask,
		Identity:         selfIdentity,
		Filter:           filter,
		Pass:             pass,
		IdentityDistance: 2,

		Timestamp: info.Time,
	}, true)
	//fmt.Println("plan continaer", positions, content.Positions)
	if positions == nil || len(positions) == 0 {
		// 无有效路径
		return false
	}
	if !isStart {
		positions = append([][]byte{current}, positions...)
	}
	c := &cooperate.MoveScheduleContent{
		Plan:            content.Plan,
		PlanIndex:       content.PlanIndex,
		SelfMask:        content.SelfMask,
		OtherMask:       content.OtherMask,
		Filter:          content.Filter,
		TargetPositions: content.TargetPositions,
		Type:            content.Type,
		Status:          content.Status,
		Logic:           content.Logic,
		LastPosition:    content.LastPosition,
		Touch:           content.Touch,
	}
	c.PlanIndex += 1
	c.Index = 0
	c.Positions = positions
	// 避免2次时间太近，导致更新异常
	// 创建新的schedule，外部是引用关系，修改时间无效
	ii := structure.ScheduleInfo{
		MapNoId:       info.MapNoId,
		Schedule:      info.Schedule,
		From:          structure.NewBaseDataFrom(info.From),
		Target:        structure.NewBaseDataFrom(info.Target),
		Key:           info.Key,
		Time:          info.Time,
		Params:        info.Params,
		Version:       info.Version,
		ScheduleData:  info.ScheduleData,
		ScheduleDelay: info.ScheduleDelay,
		Depend:        info.Depend,
	}
	builder := logic.ScheduleBuilder()
	builder.BindInfo(ii).Move(c).Schedule(ii.Time, ii.Params, ii.ScheduleDelay)
	return true
}

func (logic *TypeMap) autoPath(current []byte, position []byte, filter [][]byte, pass [][]byte, info *structure.ScheduleInfo, content *cooperate.MoveScheduleContent, selfIdentity int, visible TargetVisible) bool {
	request := &pathfinding.StrategyBlockRequest{
		Goal:    position,
		Visible: utils.ToInt(visible),
		Arrive:  true,
		Item:    info.Target.GetItem(),
		PreSeat: true,
		From:    current,
		Mask: &pathfinding.StrategyMask{
			Self:             content.SelfMask,
			Other:            content.OtherMask,
			Identity:         selfIdentity,
			Filter:           filter,
			Pass:             pass,
			IdentityDistance: 100,
			Timestamp:        info.Time,
		},
	}
	positions := logic.OnAutoFinding(current, position, info.Time, request)
	//fmt.Println("auto", info.Target, positions, content.Status)
	// 只有一个节点，当前就能停留
	if positions == nil || len(positions) <= 1 {
		// 无有效路径
		return false
	}
	c := &cooperate.MoveScheduleContent{
		SelfMask:        content.SelfMask,
		OtherMask:       content.OtherMask,
		Filter:          content.Filter,
		TargetPositions: content.TargetPositions,
		Type:            content.Type,
		Status:          content.Status,
		Logic:           content.Logic,
		LastPosition:    content.LastPosition,
		Touch:           content.Touch,
	}

	c.Plan = [][]byte{current, positions[len(positions)-1]}
	c.PlanIndex = 0
	c.Index = 0
	c.Positions = positions
	// 等待1s
	// 继续执行：修改time会在updateSchedule内部变更info引用，需要存储后还原城当前值
	ii := &structure.ScheduleInfo{
		MapNoId:       info.MapNoId,
		Schedule:      info.Schedule,
		From:          structure.NewBaseDataFrom(info.From),
		Target:        structure.NewBaseDataFrom(info.Target),
		Key:           info.Key,
		Time:          logic.CurrentTime() + 1000,
		Params:        info.Params,
		Version:       info.Version,
		ScheduleData:  info.ScheduleData,
		ScheduleDelay: info.ScheduleDelay,
		Depend:        info.Depend,
	}
	// 1000保证后续wait的索引不错
	builder := logic.ScheduleBuilder()
	builder.BindInfo(*ii).Move(c).Schedule(ii.Time, ii.Params, ii.ScheduleDelay)
	return true
}

func (logic *TypeMap) findPath(current []byte, position []byte, filter [][]byte, pass [][]byte, info *structure.ScheduleInfo, content *cooperate.MoveScheduleContent, selfIdentity int, visible TargetVisible) bool {
	var plan [][]byte
	var positions [][]byte
	plan, positions = logic.OnPlanFinding(current, position, &pathfinding.StrategyMask{
		Self:            content.SelfMask,
		Other:           content.OtherMask,
		Identity:        selfIdentity,
		Filter:          filter,
		Pass:            pass,
		TargetPositions: content.TargetPositions,
	}, true)
	fmt.Println("plan", current, position, filter, pass, plan, positions, content.TargetPositions)
	if positions == nil || len(positions) == 0 {
		// 无有效路径
		return false
	}
	if !bytes.Equal(plan[0], positions[0]) {
		fmt.Println("find plan", info.Target, current, position, filter, pass, plan, positions)
	}

	c := &cooperate.MoveScheduleContent{
		SelfMask:        content.SelfMask,
		OtherMask:       content.OtherMask,
		Filter:          content.Filter,
		TargetPositions: content.TargetPositions,
		Type:            content.Type,
		Status:          content.Status,
		Logic:           content.Logic,
		LastPosition:    content.LastPosition,
		Touch:           content.Touch,
	}

	c.Plan = plan
	c.PlanIndex = 0
	c.Index = 0
	c.Positions = positions

	ii := structure.ScheduleInfo{
		MapNoId:       info.MapNoId,
		Schedule:      info.Schedule,
		From:          structure.NewBaseDataFrom(info.From),
		Target:        structure.NewBaseDataFrom(info.Target),
		Key:           info.Key,
		Time:          logic.CurrentTime() + 1000,
		Params:        info.Params,
		Version:       info.Version,
		ScheduleData:  info.ScheduleData,
		ScheduleDelay: info.ScheduleDelay,
		Depend:        info.Depend,
	}
	builder := logic.ScheduleBuilder()
	builder.BindInfo(ii).Move(c).Schedule(ii.Time, ii.Params, ii.ScheduleDelay)
	return true
}

func (logic *TypeMap) setBlock(instance cooperate.PropsInstance, position []byte, nextPosition []byte, nnPosition []byte, leaveTime int64, stayTime int64, selfIdentity int, visible TargetVisible) {
	//blockTime := BlockTime         // + utils.ToString(visible)
	//blockIdentity := BlockIdentity // + utils.ToString(visible)
	//stopStatus := StopStatus       // utils.ToString(visible)
	extend := &pathfinding.StrategyExtend{
		Time:    stayTime,
		Visible: int(visible),
		Next:    nnPosition,
	}
	abilityProp := ReceiveAbilityProp(instance)

	//fmt.Println("block", target, position, nextPosition, nnPosition, leaveTime, stayTime, selfIdentity)
	logic.MoveSeat(instance.GetItem(), position, nextPosition, leaveTime, selfIdentity, abilityProp.View, extend)
}

func (logic *TypeMap) bindBehavior(instance cooperate.PropsInstance, behaviors BehaviorSlice, attackDistance int) BehaviorSlice {
	attackSpeed := instance.AttrWithId(logic.AttackSpeed)
	attackSpeedChange := instance.AttrWithId(logic.AttackSpeedChange)
	attackSpeed = instance.AttrAddition(attackSpeed, attackSpeedChange)

	if behaviors == nil {
		behaviors = NewBehaviorSliceWithBytes(instance.ToBytes(Behaviors))
	}
	for index := range behaviors {
		direct, ok := logic.Center.DirectMap[behaviors[index].Id]
		if !ok {
			continue
		}
		behaviors[index].Bind(direct)
	}
	// 绑定基础攻击距离和速度，替换属性
	behaviors.BindBase(byte(attackDistance), attackSpeed)

	//fmt.Println("direct", behaviors, attackSpeed)
	return behaviors
}

func (logic *TypeMap) switchMove(info *structure.ScheduleInfo, content *cooperate.TouchScheduleContent, instance cooperate.PropsInstance, position []byte) {
	move := content.Move
	if move == nil {
		move = &cooperate.MoveScheduleContent{}
		key := instance.ToString(Key)
		id := logic.Center.TargetIdMap[info.Target.DataType][key]
		switch info.Target.DataType {
		case UnitType:
			unit := logic.Center.UnitMap[id]
			move.SelfMask = unit.SelfMask
			move.OtherMask = unit.OtherMask
		default:
			return
		}
	}
	move.Touch = content
	move.Logic = content.Logic
	move.Type = content.Type
	move.Status = Normal

	if len(info.Params) >= 3 {
		event := utils.ToByte(info.Params[2])
		// 驻守触发逻辑：设定最后返回坐标
		if event == ListenSystemEvent && (move.LastPosition == nil || len(move.LastPosition) == 0) {
			move.LastPosition = position
		}
	}

	//fmt.Println("switch move", info.Target, content.Position, content.Target, move, info.Time, info.Params)
	content.Move = nil
	content.Logic = []byte{}

	ii := structure.ScheduleInfo{
		MapNoId:       info.MapNoId,
		Schedule:      cooperate.MoveSchedule,
		From:          structure.NewBaseDataFrom(info.From),
		Target:        structure.NewBaseDataFrom(info.Target),
		Key:           info.Key,
		Time:          info.Time,
		Params:        info.Params,
		Version:       info.Version,
		ScheduleData:  info.ScheduleData,
		ScheduleDelay: info.ScheduleDelay,
		Depend:        info.Depend,
	}
	logic.Move(&ii, move)
	//builder := logic.ScheduleBuilder()
	//builder.BindInfo(ii).Move(move).Schedule(ii.Time, nil, ii.ScheduleDelay)
}

func (logic *TypeMap) switchAttack(info *structure.ScheduleInfo, content *cooperate.MoveScheduleContent, position []byte, target *structure.BaseData) {
	touch := content.Touch
	//fmt.Println("switch attack", info.Target, content, touch, info.Time)
	if touch == nil {
		touch = &cooperate.TouchScheduleContent{}
	}
	touch.Move = content
	touch.Logic = content.Logic
	touch.Type = content.Type
	touch.Position = position
	touch.Times = -1
	touch.Target = target

	content.Touch = nil
	content.Logic = []byte{}

	ii := structure.ScheduleInfo{
		MapNoId:       info.MapNoId,
		Schedule:      cooperate.TouchSchedule,
		From:          structure.NewBaseDataFrom(info.From),
		Target:        structure.NewBaseDataFrom(info.Target),
		Key:           info.Key,
		Time:          info.Time,
		Params:        info.Params,
		Version:       info.Version,
		ScheduleData:  info.ScheduleData,
		ScheduleDelay: info.ScheduleDelay,
		Depend:        info.Depend,
	}
	logic.Touch(&ii, touch)
	//builder := logic.ScheduleBuilder()
	//builder.BindInfo(ii).Touch(touch).Schedule(ii.Time, ii.Params, ii.ScheduleDelay)
}

func (logic *TypeManager) getAttackInfo(target *cooperate.TouchTarget, currentTime int64) *AttackObject {
	ap := target.Receive(AbilityPropType)
	if ap == nil {
		fmt.Println("ability props miss", target)
		return nil
	}
	abilityProp, ok := ap.(*AbilityProp)
	if !ok {
		fmt.Println("ability props miss", target)
		return nil
	}
	a := &AttackObject{}
	a.Id = target.GetId()
	a.DataType = target.GetDataType()
	a.Identity = target.Identity
	a.Position = target.Position
	a.Key = target.ToString(Key)
	a.Direction = target.ToBytes(Direction)
	a.Crossed = target.ToInt64(CrossedTime) > currentTime
	a.BeCross = target.ToInt64(BeCrossTime) > currentTime
	a.Visible = TargetVisible(target.ToByte(Visible))
	readyBehavior := cooperate.NewSustainBy(target.GetProp(ReadyBehaviorSustain))
	if readyBehavior != nil && readyBehavior.Time > currentTime {
		a.Readying = true
	}

	a.attr = abilityProp.AttrAbility.ListToMap()

	m := logic.Center.GetDataStatMap()
	// 写入data数据，到attr
	for _, aa := range m {
		a.attr.Append(aa.Id, utils.ToInt(target.GetProp(aa.Key)))
	}

	a.AttrAbility = a.attr.ToList()

	buffAbility := abilityProp.BuffAbility
	a.BuffAbility = buffAbility

	a.BuffAbilityExtend = abilityProp.BuffAbilityExtend
	return a
}

func (logic *TypeMap) attackNotice(from *cooperate.TouchTarget, target *cooperate.TouchTarget, time int64) {
	aiMode := AiModeDefine(target.ToByte(AiMode))
	if aiMode == StopAiMode {
		return
	}
	value := logic.GetEventCache(logic, target, BeAttackSystemEvent)
	if value != nil {
		//fmt.Println("attack notice", target.BaseData(), info.Target)
		logic.Mode(logic, from, target, *value, time, cooperate.NewModeExtend([]interface{}{from.GetDataType(), from.GetId(), BeAttackSystemEvent}, target.GetMapNoId(), nil, target.Position))
	}
}

func (logic *TypeMap) getTouchInfo(instance cooperate.PropsInstance, distance int) (*cooperate.TouchTarget, TargetVisible) {
	p := instance.ToBytes(Position)
	if p == nil || len(p) == 0 {
		debug.PrintStack()
		fmt.Println("get touch info", instance.Data())
	}
	key := instance.ToString(Key)
	id := 0
	if key != "" {
		id = logic.Center.TargetIdMap[instance.GetDataType()][key]
	}
	visible := TargetVisible(instance.ToInt(Visible))
	tIdentity := instance.ToInt(Identity)
	if instance.GetDataType() == NodeType {
		tIdentity = 0
	}
	target := &cooperate.TouchTarget{
		PropsInstance: instance,
		Position:      p,
		TypeId:        id,
		Distance:      distance,
		Identity:      tIdentity,
	}
	return target, visible
}
func (logic *TypeMap) defaultTarget(mapNoId string, touch *cooperate.TouchScheduleContent, selfIdentity int, position []byte, params []interface{}, time int64, selectLogic SelectLogic) *cooperate.TouchTarget {
	var touchTarget *cooperate.TouchTarget
	var visible TargetVisible
	// 关联基础目标
	if touch != nil && touch.Target != nil {
		targetInfo := logic.GetDetailCache(logic, touch.Target)
		if targetInfo != nil {
			//fmt.Println("default", targetInfo.ToBytes(Position), position, logic.getDistance(targetInfo, position))
			touchTarget, visible = logic.getTouchInfo(targetInfo, logic.getDistance(targetInfo, position))
		}
	}
	if touchTarget == nil && params != nil && len(params) >= 2 {
		targetInfo := logic.GetDetailCache(logic, structure.NewBaseData(structure.PublicDataType(utils.ToByte(params[0])), params[1]))
		if targetInfo != nil {
			//fmt.Println("param target, params", params)
			touchTarget, visible = logic.getTouchInfo(targetInfo, logic.getDistance(targetInfo, position))
			if touch != nil {
				// attack和move转换减少查询
				touch.Target = targetInfo.Data()
			}
		}
	}
	if touchTarget != nil {
		// 有攻击目标, 获取目标信息
		targetInfo := touchTarget.PropsInstance
		if targetInfo == nil {
			// 目标不存在
			touchTarget = nil
		} else if targetInfo.GetDataType() == NodeType {
			// 如果目标是点，无需判断
			// 判断攻击是否支持节点攻击
		} else {
			if visible != NormalTargetVisible {
				touchTarget = nil
				goto Return
			}
			if targetInfo.ToBool(Ignore) {
				touchTarget = nil
				goto Return
			}
			if targetInfo.ToInt(Health) <= 0 {
				touchTarget = nil
				goto Return
			}
			touchTarget.Position = targetInfo.ToBytes(Position)
			if len(touchTarget.Position) == 0 {
				touchTarget = nil
				goto Return
			}
			if !logic.IsTargetIdentity(selfIdentity, touchTarget.Identity, selectLogic.GetIdentity()) {
				touchTarget = nil
				goto Return
			}
			//fmt.Println("select target", target, selectLogic.IsTarget(target))
			if !selectLogic.IsTarget(touchTarget) {
				touchTarget = nil
				goto Return
			}
			abilityProp := ReceiveAbilityProp(targetInfo)
			safe, safeTime := abilityProp.GetAttr(logic.Safe)
			if safe > 0 && utils.ToInt64(safeTime)*1000 > time {
				// 不可攻击
				touchTarget = nil
			}
		}
	}
Return:
	return touchTarget
}

type SelectLogic interface {
	IsTarget(target *cooperate.TouchTarget) bool
	GetNumber() int
	GetIdentity() int
}

func (logic *TypeMap) selectTarget(targets []*cooperate.TouchTarget, mode TargetModeDefine, selfIdentity int, selectLogic SelectLogic) []*cooperate.TouchTarget {
	// 先过滤，再排序
	var q utils.PriorityHeapQueue
	if mode&DescTargetMode > 0 {
		q = utils.NewReNodeQueue()
	} else {
		q = utils.NewNodeQueue()
	}
	number := selectLogic.GetNumber()
	//fmt.Println("select", targets, number)
	for index, target := range targets {
		//fmt.Println("select identity", selfIdentity, target.Identity, selectLogic.GetIdentity(), logic.IsTargetIdentity(selfIdentity, target.Identity, selectLogic.GetIdentity()))
		if !logic.IsTargetIdentity(selfIdentity, target.Identity, selectLogic.GetIdentity()) {
			continue
		}
		//fmt.Println("select target", target, selectLogic.IsTarget(target))
		if !selectLogic.IsTarget(target) {
			continue
		}
		i := utils.Int(index)
		if mode&DistanceTargetMode > 0 {
			q.Push(&i, target.Distance)
		} else if mode&HealthTargetMode > 0 {
			q.Push(&i, target.ToInt(Health))
		} else {
			// 随机获取目标
			q.Push(&i, rand.Intn(len(targets)))
		}
	}
	if q.Len() < number {
		number = q.Len()
	}
	tmp := make([]*cooperate.TouchTarget, 0, number)
	for q.Len() > 0 {
		a, _ := q.Pop()
		tmp = append(tmp, targets[a.(*utils.Int).Value()])
	}
	return tmp
}

type FindLogic interface {
	GetRange() TargetRange
	GetDistance() int
	GetPosition() []byte
	CanVisible() bool
}

func (logic *TypeMap) findTarget(info InfoContainer, find FindLogic, position []byte, routePositions [][]byte, self cooperate.PropsInstance) []*cooperate.TouchTarget {
	// 找寻新的攻击目标
	//result := make(chan []interface{})
	var allPositions [][]byte
	if routePositions == nil {
		routePositions = [][]byte{}
	}
	switch find.GetRange() {
	case DistanceTargetRange:
		allPositions = logic.RangeDistance(position, int(find.GetDistance()))
		if len(routePositions) > find.GetDistance() {
			routePositions = routePositions[0:find.GetDistance()]
		}
	case PositionTargetRange:
		targetPosition := find.GetPosition()
		if logic.Distance(position, targetPosition) <= find.GetDistance() {
			allPositions = [][]byte{find.GetPosition()}
			routePositions = [][]byte{}
		}
	}
	//fmt.Println("find", allPositions)
	targets := make([]*cooperate.TouchTarget, 0, len(allPositions))
	if len(allPositions) > 0 {
		typeList, itemList, _ := logic.GetItemList(allPositions, info.GetTime())
		if itemList == nil {
			return targets
		}
		selfIdentity := self.ToInt(Identity)
		mask := 0
		key := self.ToString(Key)
		id := logic.Center.TargetIdMap[self.GetDataType()][key]
		switch self.GetDataType() {
		case UnitType:
			unit := logic.Center.UnitMap[id]
			mask = unit.AttackRangeMask
			if len(routePositions) > 0 {
				for _, p := range routePositions {
					if bytes.Equal(p, position) {
						mask |= unit.AttackRouteMask
						break
					}
				}
			}
		case BuildType:
			build := logic.Center.BuildMap[id]
			mask = build.AttackRangeMask
		case ItemType:
			item := logic.Center.ItemMap[id]
			mask = item.AttackRangeMask
		}
		for index, item := range itemList {
			if item == nil {
				continue
			}
			r := structure.NewBaseDataByte(item)
			current := info.GetTarget()
			if r == nil || (r.DataType == current.DataType && utils.ToInt(r.Id) == utils.ToInt(current.Id)) {
				continue
			}
			pp := logic.GetDetailCache(logic, r)
			if pp == nil {
				continue
			}
			if mask > 0 && (1<<(typeList[index]-1))&mask == 0 {
				continue
			}
			if pp.ToBool(Ignore) {
				continue
			}
			if pp.ToInt(Health) <= 0 {
				continue
			}
			// 使用当前寻路中坐标
			//pp["Position"] = utils.ToBytes(pp[Position])
			//pp[Position] = positions[index]
			//pp.MergeProp(Position, positions[index])
			//distance := logic.Distance(position, positions[index])
			//if target.Distance > 4 && target.GetDataType() == UnitType {
			//	fmt.Println("distance diff", position, positions[index], utils.ToBytes(Position), info.Target, r, r.DataType == info.Target.DataType, r.Id == info.Target.Id, target.Position, target.Distance)
			//	//panic("distance diff")
			//}
			target, visible := logic.getTouchInfo(pp, logic.Distance(position, allPositions[index]))
			abilityProp := ReceiveAbilityProp(pp)
			if visible != NormalTargetVisible {
				isFriend, isEmery := logic.JudgeIdentity(selfIdentity, target.Identity)
				//fmt.Println(isFriend, isEmery)
				if isFriend {

				} else if isEmery && target.Distance <= abilityProp.GetAttrWithId(logic.SearchDistance) {
					// 发现
					if find.CanVisible() {
						logic.changeVisible(info, target.Position, target.Data(), target.Identity, true)
					}
				} else {
					// 过滤
					continue
				}
			}
			//fmt.Println("target", target)
			targets = append(targets, target)
		}
	}
	return targets
}

func (logic *TypeMap) findTargetRange(self cooperate.PropsInstance, direct *model.Direct, target cooperate.PropsInstance) ([]*cooperate.TouchTarget, [][]byte) {
	if direct.TargetMode == model.RangeTargetMode {
		baseInfo := &BaseInfo{
			MapNoId: logic.GetMapNoId(),
			From:    self.Data(),
			Target:  self.Data(),
			Time:    logic.CurrentTime(),
		}
		position := target.ToBytes(Position)
		baseFind := &BaseFind{
			Range:    DistanceTargetRange,
			Distance: int(direct.Range),
			Position: position,
			Visible:  false,
		}
		return logic.findTarget(baseInfo, baseFind, position, nil, self), [][]byte{position}
	} else {
		return nil, nil
	}
}

func (logic *TypeMap) attackTarget(targetMode TargetModeDefine, selectLogic SelectLogic, self *cooperate.TouchTarget, targets []*cooperate.TouchTarget, target *cooperate.TouchTarget, parter *cooperate.TouchTarget) []*cooperate.TouchTarget {
	var selectTargets []*cooperate.TouchTarget
	if selectLogic.GetNumber() != 1 {
		selectTargets = logic.selectTarget(targets, targetMode, self.Identity, selectLogic)
		//if selectTargets == nil || len(selectTargets) == 0 {
		//	// 目标移动过远，但还是可以打到
		//	if target != nil {
		//		selectTargets = []*cooperate.TouchTarget{target}
		//	}
		//}
	} else {
		identity := selectLogic.GetIdentity()
		if utils.IncludeIntByte(identity, int(SelfTargetIdentity)) {
			selectTargets = append(selectTargets, self)
		} else if utils.IncludeIntByte(identity, int(EnemyTargetIdentity)) {
			if target != nil {
				_, isE := logic.JudgeIdentity(self.Identity, target.Identity)
				if !isE {
					target = nil
				}
			}
			if target == nil {
				tmp := logic.selectTarget(targets, targetMode, self.Identity, selectLogic)
				if tmp != nil && len(tmp) > 0 {
					target = tmp[0]
				}
			}
			if target != nil {
				selectTargets = []*cooperate.TouchTarget{target}
			}
		} else if utils.IncludeIntByte(identity, int(PartnerTargetIdentity)) {
			if parter == nil {
				if target != nil {
					isP, _ := logic.JudgeIdentity(self.Identity, target.Identity)
					if isP {
						parter = target
					}
				}
			}
			if parter == nil {
				tmp := logic.selectTarget(targets, targetMode, self.Identity, selectLogic)
				if tmp != nil && len(tmp) > 0 {
					parter = tmp[0]
				}
			}
			if parter != nil {
				selectTargets = []*cooperate.TouchTarget{parter}
			}
		}
	}
	return selectTargets
}

type InfoContainer interface {
	GetMapNoId() string
	GetFrom() *structure.BaseData
	GetTarget() *structure.BaseData
	GetTime() int64
}

func (logic *TypeMap) executeAttack(from structure.Data, self *cooperate.TouchTarget, targets []*cooperate.TouchTarget, targetPositions [][]byte, id int, time int64, position []byte) {
	value := logic.GetEventCache(logic, self, AttackSystemEvent)
	//fmt.Println("execute attack", self, value, len(targets), time)
	if value != nil {
		selfInfo := logic.getAttackInfo(self, time)
		targetInfoList := make([]*AttackObject, len(targets))
		for index, target := range targets {
			targetInfoList[index] = logic.getAttackInfo(target, time)
			//fmt.Println("target", self.Data(), position, target.Data(), targetInfoList[index].Position)
			logic.attackNotice(self, target, time)
		}
		if value.Mode != 2 {
			fmt.Println("attack mode", self, value.Mode, id, position, targetInfoList)
		}
		logic.Mode(logic, from, self, *value, time, cooperate.NewModeExtend([]interface{}{id, position, selfInfo, targetInfoList, targetPositions}, self.GetMapNoId(), nil, position))
	}
}

func (logic *TypeMap) changeCombat(info InfoContainer, combat bool) []cooperate.UpdateOperateContent {
	if combat {
		// 设定攻击状态
		// 攻击会首先到达
		// 被攻击，会触发攻击指令，如果没有技能发放应该是在攻击中
		return []cooperate.UpdateOperateContent{{
			Key:     Combat,
			Operate: cooperate.ChangeData,
			Value:   1,
		}, {
			Key:     "leaveCombat",
			Operate: cooperate.RemoveSchedule,
		}}
	}
	// 脱离攻击状态：攻击次数到达，目标为空
	if err := logic.AddOnceSchedule(info.GetMapNoId(), &cooperate.ScheduleContent{
		From:   info.GetFrom(),
		Target: info.GetTarget(),
		Key:    "leaveCombat",
		Time:   info.GetTime() + int64(HoldBattleTime),
	}, logic.MakeOperateEvent([]cooperate.OperateData{
		cooperate.NewOperateData(cooperate.BatchUpdateOperate, &cooperate.UpdateOperateData{
			Data: *info.GetTarget(),
			Operates: []cooperate.UpdateOperateContent{{
				Key:     Combat,
				Operate: cooperate.ChangeData,
				Value:   0,
			}},
		}),
	}, nil), nil); err != nil {
		fmt.Println("leave combat err", err)
	}
	return nil
}

func (logic *TypeMap) getDistance(instance cooperate.PropsInstance, position []byte) int {
	fixProp := ReceiveFixProp(instance)
	if fixProp == nil {
		return logic.Distance(position, instance.ToBytes(Position))
	}
	return logic.MinDistance(fixProp.Positions, position)
}

func (logic *TypeMap) getPositions(instance cooperate.PropsInstance) [][]byte {
	id := logic.Center.TargetIdMap[instance.GetDataType()][instance.ToString(Key)]
	r := 0
	switch instance.GetDataType() {
	case BuildType:
		build := logic.Center.BuildMap[id]
		r = build.FixRange
	case UnitType:
		r = 1
	}
	return logic.RangeDistance(instance.ToBytes(Position), r-1)
}

func (logic *TypeMap) changeVisible(info InfoContainer, position []byte, target *structure.BaseData, identity int, force bool) []cooperate.UpdateOperateContent {
	operates := []cooperate.UpdateOperateContent{{
		Operate: cooperate.ChangeData,
		Key:     Visible,
		Value:   NormalTargetVisible,
	}}
	if force {
		operateData := &cooperate.UpdateOperateData{
			Data:     *target,
			Operates: operates,
		}
		logic.scheduleBatchUpdateOperate2(info, operateData, 0)
	}

	// 触发
	logic.triggerListen(target, position, info.GetTime())
	return operates
}

func (logic *TypeMap) triggerListen(target *structure.BaseData, position []byte, currentTime int64) {
	nl := logic.GetLinksBy(ListenRelation, position)
	for _, pp := range nl {
		r := structure.NewBaseDataByte(pp)
		// 通知发现
		event := logic.GetEventCache(logic, r, ListenSystemEvent)
		//fmt.Println("listen", position, r, event)
		if event != nil {
			logic.Mode(logic, target, r, *event, currentTime, cooperate.NewModeExtend([]interface{}{target.DataType, target.Id, ListenSystemEvent}, logic.GetMapNoId(), nil, position))
		}
	}
}

func (logic *TypeMap) Listen(info InfoContainer, listen *ListenLogic) {
	result := logic.GetPropsCache(logic, info.GetTarget(), []string{Identity})
	if result == nil || len(result) == 0 {
		fmt.Println("listen miss", info.GetTarget())
		return
	}
	selfIdentity := utils.ToInt(result[Identity])
	logic.OnListen(info.GetTarget(), selfIdentity, listen, info.GetTime())
}

func (logic *TypeMap) Search(info InfoContainer, search *SearchLogic) {
	result := logic.GetPropsCache(logic, info.GetTarget(), []string{Identity})
	if result == nil || len(result) == 0 {
		return
	}
	selfIdentity := utils.ToInt(result[Identity])
	_, itemList, identityList := logic.GetItemList(search.Nodes, info.GetTime())
	//fmt.Println("item:", itemList, identityList)
	if itemList == nil || identityList == nil {
		return
	}
	for _, item := range itemList {
		if item == nil {
			continue
		}
		r := structure.NewBaseDataByte(item)
		if r.Equal(info.GetTarget()) {
			continue
		}
		pp := logic.GetDetailCache(logic, r)
		if pp.ToBool(Ignore) {
			continue
		}
		tIdentity := pp.ToInt(Identity)
		tVisible := TargetVisible(pp.ToInt(Visible))
		position := pp.ToBytes(Position)
		isPartner, _ := logic.JudgeIdentity(selfIdentity, tIdentity)
		if isPartner {
			continue
		}
		if tVisible == NormalTargetVisible {
			continue
		}
		logic.changeVisible(info, position, pp.Data(), tIdentity, true)
	}
}

func (logic *TypeMap) Snipe(info InfoContainer, snipe *SnipeLogic) {
	// 判断周围的视野，如果
	instance := logic.GetDetailCache(logic, snipe.Target)
	position := instance.ToBytes(Position)
	view := instance.ToInt(View)

	target := info.GetTarget()
	fromInstance := logic.GetDetailCache(logic, info.GetTarget())
	fromPosition := fromInstance.ToBytes(Position)
	identity := fromInstance.ToInt(Identity)

	// todo 如何判断查询范围
	if logic.HasViewOther(position, fromPosition, identity, view, logic.FormatTime(info.GetTime())) {
		// 退出隐身
		logic.changeVisible(info, fromPosition, target, identity, true)
	}
}

func (logic *TypeMap) scheduleBatchUpdateOperate1(info InfoContainer, data *cooperate.UpdateOperateData, next int64) {
	updateData := cooperate.OperateContent{
		From:    structure.NewBaseDataFrom(info.GetFrom()),
		Target:  structure.NewBaseDataFrom(info.GetTarget()),
		MapNoId: info.GetMapNoId(),
		Operates: []cooperate.OperateData{
			cooperate.NewOperateData(cooperate.BatchUpdateOperate, data),
		},
	}

	next += info.GetTime()
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 7)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) scheduleOperate1(info *structure.ScheduleInfo, data []cooperate.OperateData, next int64) {
	updateData := cooperate.OperateContent{
		From:     structure.NewBaseDataFrom(info.GetFrom()),
		MapNoId:  info.MapNoId,
		Target:   structure.NewBaseDataFrom(info.GetTarget()),
		Operates: data,
	}

	next += info.Time
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 6)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) scheduleMessageOperate1(info *structure.ScheduleInfo, data *cooperate.OperateMessage, next int64) {
	updateData := cooperate.OperateContent{
		From:    structure.NewBaseDataFrom(info.GetFrom()),
		MapNoId: info.MapNoId,
		Target:  structure.NewBaseDataFrom(info.GetTarget()),
		Message: data,
	}

	next += info.Time
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 5)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) scheduleBatchUpdateOperate2(info InfoContainer, data *cooperate.UpdateOperateData, next int64) {
	updateData := cooperate.OperateContent{
		From:    structure.NewBaseDataFrom(info.GetFrom()),
		MapNoId: info.GetMapNoId(),
		Target:  structure.NewBaseDataFrom(info.GetTarget()),
		Operates: []cooperate.OperateData{
			cooperate.NewOperateData(cooperate.BatchUpdateOperate, data),
		},
	}

	next += info.GetTime()
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 4, data)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) scheduleOperate2(info InfoContainer, data []cooperate.OperateData, next int64) {
	updateData := cooperate.OperateContent{
		From:     structure.NewBaseDataFrom(info.GetFrom()),
		MapNoId:  info.GetMapNoId(),
		Target:   structure.NewBaseDataFrom(info.GetTarget()),
		Operates: data,
	}

	next += info.GetTime()
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 3)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) scheduleMessageOperate2(info InfoContainer, data *cooperate.OperateMessage, next int64) {
	updateData := cooperate.OperateContent{
		From:    structure.NewBaseDataFrom(info.GetFrom()),
		MapNoId: info.GetMapNoId(),
		Target:  structure.NewBaseDataFrom(info.GetTarget()),
		Message: data,
	}

	next += info.GetTime()
	//if logic.FromStartUp(next)-logic.FromStartUp(logic.CurrentTime()) < -500 {
	//	debug.PrintStack()
	//}
	//fmt.Println("operate push", info.Target, next, next-logic.CurrentTime(), 2)
	logic.PushOperate(updateData, next)
}

func (logic *TypeMap) operateOne(mapNoId string, target *structure.BaseData, operates []cooperate.UpdateOperateContent, delay int64) {
	operateData := &cooperate.UpdateOperateData{
		Data:     *target,
		Operates: operates,
	}
	updateData := cooperate.OperateContent{
		MapNoId: mapNoId,
		Target:  structure.NewBaseDataFrom(target),
		Operates: []cooperate.OperateData{
			cooperate.NewOperateData(cooperate.BatchUpdateOperate, operateData),
		},
	}
	logic.PushOperate(updateData, delay)
}
